package doc2db.fileWorker.xml;

import doc2db.fileWorker.ParseHelper;
import doc2db.fileWorker.ParseHelperInterface;
import doc2db.fileWorker.xml.*;
import doc2db.table.DocTable;
import doc2db.table.DocTableCell;
import java.util.ArrayList;
import java.util.List;



/**
 * Класс для обработки строк и выдиления в них интересующей
 * информации , если структура xml шаблона древовидная .
 * Он хранит последовательности и уровни вложености .
 *
 *
 * @author yura
 */
public class TreeParseHelper implements ParseHelperInterface {
    /**Для обработки строк*/
    private ParseHelper ph ;
    /**Список ячеек которые следует добавить к таблице*/
    private List<TreeElem> list = new ArrayList<TreeElem>(){
        @Override
        public boolean add(TreeElem e) {
            for (int i = 0; i < this.size(); i++) {
                final String s1 = this.get(i).text;
                final String s2 = e.text;
                if(s1.equals(s2))
                    return false;
            }
            return super.add(e);
        }
    };
    /**Дерево xml описания структуры документа*/
    private TreeTypeNodes tree;
    private boolean isInit = false;

    /**
     * Конструктор
     * @param ph Для обработки строк
     * @param tree xml описания структуры документа
     */
    public TreeParseHelper(ParseHelper ph, TreeTypeNodes tree) {
        this.ph = ph;
        this.tree = tree;
        this.initList(tree);
    }

    /**
     * Заполняет список элементами, начиная с указанной ветви
     * дерева или корня дерева и позиции . Берётся текстовое
     * значение на данной ветви и функция вызывает сама себя ,
     * относительно первой порождаемой ветви данного узла и
     * значенеим позиции в списке на еденицу выше . Это продолжается
     * до тех пор пока не встрется конечная ветвь , тогда она
     * и все соседние элементы добавлятся в список.
     * @param node
     * @param begin
     */
    private void initList(TreeTypeNodes node){
        list = this.sort(list);
        int begin = node.getDegreeInward();

        for (int i = 0; i < list.size(); i++) {
            if(list.get(i).degree >= begin){
                list.remove(i);
                i--;
                continue;
            }
        }

        if(! node.isLastElem()){
            list.add(new TreeElem(node));
            this.initList(node.getElemInList(0));
        }else{
            for (TreeTypeNodes tr : node.getFather().getList()) {
                list.add(new TreeElem(tr));
            }
        }
    }

    public boolean initListBeginValue(StringBuffer buffer){
        if(! isInit){
            for (TreeElem te : list) {
                te.setValue(ph.getInfoInSuggestion(buffer, new String[]{te.text}, false));
            }
            isInit = true;
            return true;
        }else{
            return false;
        }
    }

    /**
     * Распечятывает информацию в обрабатываемой очереди 
     */
    public void print(){
        System.out.println("list :");
        System.out.println(this);
        System.out.println("list end;");
    }

    /**
     * выдеёт данные в виде масива
     * @return список в ввиде масива строк
     */
    public String[] toStringArray(){
        String [] arr = new String[list.size()];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = list.get(i).text;
        }
        return arr;
    }

    public String[][] getInfoInSuggestion(StringBuffer sb, String[] arr, boolean tr) {
        return new String [][] {{"null"}, {"null"}};
    }

    public void addHeadCellFromDocHead(DocTable docT, String[][] str) {
        this.addHeadCellFromDocHead(docT, tree);
    }

    /**
     * Добовляет к таблице необходимые заголовки , которые
     * описанны в шаблоне
     * @param docT таблица
     * @param tr_ начяльная ветвь
     */
    private void addHeadCellFromDocHead(DocTable docT, TreeTypeNodes tr_){
        String arr [] = tr_.getText().split("\\$");
        for (int i = 0; i < arr.length; i++) {
            if(!((i%2) == 1))
                continue;
            String s = arr[i];
            boolean tr = s.equals("null");
            if (!tr) {
                try {
                    docT.addHeadCell(s);
                } catch (java.lang.ArrayIndexOutOfBoundsException e) {
                    System.err.println("  >can add head cell");
                }
            }
        }
        if(!tr_.isLastElem()){
            for (TreeTypeNodes nod : tr_.getList()) {
                this.addHeadCellFromDocHead(docT, nod);
            }
        }

    }

    /**
     * Добавляет к текущей строке таблицы найденные ячейки
     * @param docT
     * @param between
     */
    public String [][] toComplementRow(DocTable docT, StringBuffer between){
        this.toComRow(between, tree);

        int n = 0;
        for (TreeElem el : list) {
            n += el.cels.size();
        }
        String ret [][] = new String [2][n];
        n = 0;
        for (TreeElem el : list) {
            for (DocTableCell te : el.cels) {
                ret[0][n] = te.getHead();
                ret[1][n] = te.getName();
                n++;
            }
        }
        return ret;
    }

    private void toComRow(StringBuffer buff, TreeTypeNodes node){
//        System.out.println("  chek for >>" + node.getText());
        if(node.isFatherElem()){
            for (TreeTypeNodes t : node.getList()) {
                this.toComRow(buff, t);
            }
        }

        else if(node.isLastElem()){
            int degree = node.getDegreeInward();
            if (ph.isInfoInSuggestion(buff, new String[]{node.getText()}, false)) {
                list = this.sort(list);
                for (int i = 0; i < list.size(); i++) {
                    if(list.get(i).degree >= degree){
                        list.remove(i);
                        i--;
                        continue;
                    }
                }
                for (TreeTypeNodes t : node.getFather().getList()) {
                    TreeElem tr = new TreeElem(t);
                    tr.setValue(ph.getInfoInSuggestion(buff, new String[]{tr.text}, false));
                    list.add(tr);
                }
            }
        }

        else{
            int degree = node.getDegreeInward();
            if (ph.isInfoInSuggestion(buff, new String[]{node.getText()}, false)) {
                list = this.sort(list);
                for (int i = 0; i < list.size(); i++) {
                    if(list.get(i).degree >= degree){
                        list.remove(i);
                        i--;
                        continue;
                    }
                }
                TreeElem tr = new TreeElem(node);
                tr.setValue(ph.getInfoInSuggestion(buff, new String[]{tr.text}, false));
                list.add(tr);
//                System.out.println(tr);
                for (TreeTypeNodes t : node.getList()) {
                    this.toComRow(buff, t);
                }
                return;
            }
            for (TreeTypeNodes t : node.getList()) {
                this.toComRow(buff, t);
            }
        }

    }

    private List <TreeElem> sort(List<TreeElem> l){
        if(l.size() < 2)
            return  l;
        
        TreeElem arr [] = new TreeElem[l.size()];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = l.get(i);
        }

        TreeElem t ;
        for(int a = 1 ; a < arr.length ; a++){
            for(int b = arr.length - 1; b >= a ; b--){
                if(arr[b-1].degree > arr[b].degree){
                    t = arr[b-1];
                    arr[b-1] = arr[b];
                    arr[b] = t;
                }
            }
        }

        ArrayList <TreeElem> ret = new ArrayList<TreeElem>();
        for (int i = 0; i < arr.length; i++) {
            ret.add(arr[i]);
        }
        return ret;
    }

    @Override
    public String toString() {
        StringBuffer retSt = new StringBuffer();
        for (TreeElem t : list) {
            retSt.append("    [text=\"" + t.text.replaceAll("\n", "<br>") +
                    "\"; degree=" + t.degree + "; " + t.toString() + ";]\n");
        }
        return retSt.toString().substring(0, retSt.toString().length()-1);
    }

}
/**
 * Класс элемент содержащий 2 поля : текст и уровень вложености
 * @author yura
 */
class TreeElem{
    /**текст*/
    String text = "ERROR";
    /** значения по заданым параметрам */
    List<DocTableCell> cels = new ArrayList<DocTableCell>();
    /**уровень вложености*/
    int degree = -1;

    public TreeElem(String t, int d) {
        this.text = t;
        this.degree = d;
    }

    public TreeElem(TreeTypeNodes tr){
        this.text = tr.getText();
        this.degree = tr.getDegreeInward();
    }

    public void setValue (String [][] cell){
        cels.clear();
        for (int i = 0; i < cell[0].length; i++) {
            if (cell[0][i] != null && cell[1][i] != null) {
                cels.add(new DocTableCell(cell[1][i], cell[0][i]));
            }
        }
    }

    @Override
    public String toString() {
        StringBuffer ret = new StringBuffer("<");
        for (DocTableCell docTableCell : cels) {
            ret.append(docTableCell.toString());
        }
        ret.append(">");
        return ret.toString();
    }
}
